/*
 *   A task scheduler that implements Monte-Carlo simulation
 *
 *   Copyright (C) 2011 Systasis Computer Systems, Inc.
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *   
 *   $Id: ActivityView.java 107 2011-08-30 01:21:52Z jchimene@gmail.com $
 */
package com.systasis.sked.client.view;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;

import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.EditTextCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.NumberCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.regexp.shared.SplitResult;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy;
import com.google.gwt.user.cellview.client.SimplePager;
import com.google.gwt.user.cellview.client.SimplePager.TextLocation;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.DefaultSelectionEventManager;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.inject.Inject;
import com.gwtplatform.mvp.client.ViewWithUiHandlers;
import com.systasis.sked.client.presenter.Activity;
import com.systasis.sked.client.presenter.ActivityPresenter;
import com.systasis.sked.client.view.ActivityHandlers.ActivityUiHandlers;

public class ActivityView extends ViewWithUiHandlers<ActivityHandlers.ActivityUiHandlers> implements ActivityPresenter.MyView {

    @UiTemplate("Activity.ui.xml")
    public interface ActivityPanelBinder extends UiBinder<Widget, ActivityView> {
    }

    @SuppressWarnings("unused")
    private final static Logger logger = Logger.getLogger(ActivityView.class.getName());

    final private Widget activityPanel;

    /**
     * The main CellTable.
     */
    @UiField(provided = true)
    CellTable<Activity> cellTable;

    @UiField
    SimplePanel menu;

    /**
     * The pager used to change the range of data.
     */
    @UiField(provided = true)
    SimplePager pager;

    @Inject
    public ActivityView(final SimplePager.Resources pagerResources //
	    , final ActivityPanelBinder activityPanelBinder //
	    , final EventBus eventBus) {
	// Set a key provider that provides a unique key for each activity. If
	// key is used to identify activities when fields (such as the name and
	// likely duration) change.
	cellTable = new CellTable<Activity>();
	cellTable.setWidth("100%", true);

	// Create a Pager to control the table.
	/*
	 * final SimplePager.Resources pagerResources =
	 * GWT.create(SimplePager.Resources.class);
	 */
	pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 0, true);
	pager.setDisplay(cellTable);

	activityPanel = activityPanelBinder.createAndBindUi(this);
    }

    @Override
    public Widget asWidget() {
	return activityPanel;
    }

    public HasWidgets getMenu() {
	return menu;
    }

    @Override
    public void onInitialize(ListDataProvider<Activity> dataProvider) {
	// Attach a column sort handler to the ListDataProvider to sort the
	// list.
	final ListHandler<Activity> sortHandler = new ListHandler<Activity>(dataProvider.getList());
	cellTable.addColumnSortHandler(sortHandler);

	// Add a selection model so we can select cells.
	final SingleSelectionModel<Activity> selectionModel = new SingleSelectionModel<Activity>();
	cellTable.setSelectionModel(selectionModel, DefaultSelectionEventManager.<Activity> createCheckboxManager());

	cellTable.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.ENABLED);

	// cellTable.addCellPreviewHandler(new
	// CellPreviewEvent.Handler<Activity>() {
	//
	// @Override
	// public void onCellPreview(CellPreviewEvent<Activity> event) {
	// if (event.getNativeEvent().getType().contentEquals("focus")) {
	// currentContext = event.getContext();
	// }
	// }
	// });

	// Initialize the columns.
	initTableColumns(selectionModel, sortHandler);

	refresh(dataProvider);
    }

    @Override
    public void refresh(ListDataProvider<Activity> dataProvider) {
	final List<Activity> list = dataProvider.getList();
	for (final Activity a : list) {
	    if (a.getActivityName().equalsIgnoreCase(Activity.EMPTY_ACTIVITY_NAME)) {
		list.remove(a);
		break;
	    }
	}
	list.add(Activity.emptyActivity());
	dataProvider.setList(list);
	dataProvider.flush();
	cellTable.setRowData(list);
	cellTable.redraw();
    }

    @Override
    public void setUiHandlers(ActivityUiHandlers uiHandlers) {
	super.setUiHandlers(uiHandlers);
    }

    final RegExp patDurationUnit = RegExp.compile("^(\\.?\\d+)([dwm]{1})$");
    final String patDurationOnly = "^\\.?\\d+$";

    private Double daysIn(String value) {
	if (!patDurationUnit.test(value)) {
	    if (value.matches(patDurationOnly)) {
		value = value.concat("d");
	    } else {
		return 0.0;
	    }
	}

	final SplitResult splitResult = patDurationUnit.split(value);
	Double r0;
	try {
	    r0 = Double.parseDouble(splitResult.get(1));
	} catch (Exception e) {
	    return 0.0;
	}

	final String r1 = splitResult.get(2);

	// final Integer r2 = splitResult.length();
	// final String r3[] = new String[r2];
	// for (int i = 0; i < r2; i++) {
	// r3[i] = splitResult.get(i);
	// }

	if (r1.matches("d")) {
	    return r0;
	}

	if (r1.matches("w")) {
	    return r0 * 7;
	}

	if (r1.matches("m")) {
	    return r0 * 30;
	}
	return 0.0;
    }

    @SuppressWarnings("unused")
    private void initTableColumns(final SingleSelectionModel<Activity> selectionModel, ListHandler<Activity> sortHandler) {
	// Checkbox column. This table will uses a checkbox column for
	// selection.
	// Alternatively, you can call cellTable.setSelectionEnabled(true) to
	// enable mouse selection.

	final Column<Activity, Boolean> checkColumn = new Column<Activity, Boolean>(new CheckboxCell(true, false)) {
	    @Override
	    public Boolean getValue(Activity object) {
		// Get the value from the selection model.
		return selectionModel.isSelected(object);
	    }
	};
	cellTable.addColumn(checkColumn, SafeHtmlUtils.fromSafeConstant("<br/>"));
	cellTable.setColumnWidth(checkColumn, 20, Unit.PX);

	selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
	    @Override
	    public void onSelectionChange(SelectionChangeEvent event) {
		com.google.gwt.core.client.Scheduler.get().scheduleDeferred(new ScheduledCommand() {
		    @Override
		    public void execute() {
			getUiHandlers().onActivityDelete(selectionModel.getSelectedObject());
		    }
		});
	    }
	});

	/*
	 * Activity id
	 */
	final Column<Activity, Number> idColumn = new Column<Activity, Number>(new NumberCell()) {

	    @Override
	    public Integer getValue(Activity activity) {
		return (-1 == activity.getId()) ? 0 : activity.getId();
	    }
	};

	cellTable.addColumn(idColumn, "Id");
	cellTable.setColumnWidth(idColumn, 20, Unit.PX);
	idColumn.setSortable(false);

	/*
	 * Activity name
	 */
	final Column<Activity, String> activityNameColumn = new Column<Activity, String>(new EditTextCell()) {
	    @Override
	    public String getValue(Activity activity) {
		return activity.getActivityName();
	    }
	};

	cellTable.addColumn(activityNameColumn, SafeHtmlUtils.fromSafeConstant("Name"));

	cellTable.setColumnWidth(activityNameColumn, 100, Unit.PX);
	activityNameColumn.setSortable(true);
	sortHandler.setComparator(activityNameColumn, new Comparator<Activity>() {
	    @Override
	    public int compare(Activity a1, Activity a2) {
		return a1.getActivityName().compareTo(a2.getActivityName());
	    }
	});

	activityNameColumn.setFieldUpdater(new FieldUpdater<Activity, String>() {
	    @Override
	    public void update(int index, final Activity activity, String value) {
		activity.setActivityName(value);
		com.google.gwt.core.client.Scheduler.get().scheduleDeferred(new ScheduledCommand() {
		    @Override
		    public void execute() {
			getUiHandlers().onActivityChange(activity);
		    }
		});
	    }
	});

	/*
	 * Predecessors
	 */
	final EditTextCell predecessorCell = new EditTextCell();
	final EditTextCell durationLikelyCell = new EditTextCell();
	final EditTextCell durationBestCell = new EditTextCell();
	final EditTextCell durationWorstCell = new EditTextCell();

	final Column<Activity, String> predecessorColumn = new Column<Activity, String>(predecessorCell) {

	    @Override
	    public String getValue(Activity activity) {
		final String r0 = activity.getPredecessorList().toString();
		return ("[]" == r0) ? "" : r0.substring(1, r0.length() - 1);
	    }
	};

	cellTable.addColumn(predecessorColumn, SafeHtmlUtils.fromSafeConstant("Predecessors"));

	cellTable.setColumnWidth(predecessorColumn, 100, Unit.PX);
	predecessorColumn.setSortable(false);
	predecessorColumn.setFieldUpdater(new FieldUpdater<Activity, String>() {
	    @Override
	    public void update(final int index, final Activity activity, final String value) {
		predecessorCell.clearViewData(activity);
		getUiHandlers().onPredecessorChange(activity, value);
	    }
	});

	/*
	 * Resource name
	 */
	final Column<Activity, String> resourceNameColumn = new Column<Activity, String>(new EditTextCell()) {
	    @Override
	    public String getValue(Activity activity) {
		return activity.getResourceName();
	    }
	};

	cellTable.addColumn(resourceNameColumn, SafeHtmlUtils.fromSafeConstant("ResourceName"));

	cellTable.setColumnWidth(resourceNameColumn, 65, Unit.PX);
	resourceNameColumn.setSortable(false);
	resourceNameColumn.setFieldUpdater(new FieldUpdater<Activity, String>() {
	    @Override
	    public void update(int index, final Activity activity, String value) {
		activity.setResourceName(value);
		com.google.gwt.core.client.Scheduler.get().scheduleDeferred(new ScheduledCommand() {
		    @Override
		    public void execute() {
			getUiHandlers().onActivityChange(activity);
		    }
		});
	    }
	});

	/*
	 * Activity sequence
	 */
	if (false) {
	    final Column<Activity, Number> sequenceColumn = new Column<Activity, Number>(new NumberCell()) {

		@Override
		public Integer getValue(Activity activity) {
		    return (null == activity.getSequenceNumber()) ? 0 : activity.getSequenceNumber();
		}
	    };

	    cellTable.addColumn(sequenceColumn, "Seq");
	    cellTable.setColumnWidth(sequenceColumn, 20, Unit.PX);
	    sequenceColumn.setSortable(false);
	}

	/*
	 * Likely duration
	 */
	final Column<Activity, String> durationLikelyColumn = new Column<Activity, String>(durationLikelyCell) {
	    @Override
	    public String getValue(Activity activity) {
		return round(activity.getDurationLikely(), 2).toString() + "d";
	    }
	};
	cellTable.addColumn(durationLikelyColumn, SafeHtmlUtils.fromSafeConstant("Likely"));
	cellTable.setColumnWidth(durationLikelyColumn, 20, Unit.PX);
	durationLikelyColumn.setSortable(false);

	durationLikelyColumn.setFieldUpdater(new FieldUpdater<Activity, String>() {
	    @Override
	    public void update(int index, final Activity activity, String value) {
		// Push the changes into the Activity Likely duration.
		durationLikelyCell.clearViewData(activity);
		activity.setDurationLikely(daysIn(value));
		getUiHandlers().onDurationChange(activity);
	    }
	});

	/*
	 * Best duration
	 */
	final Column<Activity, String> durationBestColumn = new Column<Activity, String>(durationBestCell) {
	    @Override
	    public String getValue(Activity activity) {
		return round(activity.getDurationBest(), 2).toString() + "d";
	    }
	};
	cellTable.addColumn(durationBestColumn, SafeHtmlUtils.fromSafeConstant("Best"));
	cellTable.setColumnWidth(durationBestColumn, 20, Unit.PX);
	durationBestColumn.setSortable(false);

	durationBestColumn.setFieldUpdater(new FieldUpdater<Activity, String>() {
	    @Override
	    public void update(int index, Activity activity, String value) {
		// Push the changes into the Activity Best duration.
		durationBestCell.clearViewData(activity);
		activity.setDurationBest(daysIn(value));
		getUiHandlers().onDurationChange(activity);
	    }
	});

	/*
	 * Worst duration
	 */
	final Column<Activity, String> durationWorstColumn = new Column<Activity, String>(durationWorstCell) {
	    @Override
	    public String getValue(Activity activity) {
		return round(activity.getDurationWorst(), 2).toString() + "d";
	    }
	};
	cellTable.addColumn(durationWorstColumn, SafeHtmlUtils.fromSafeConstant("Worst"));
	cellTable.setColumnWidth(durationWorstColumn, 20, Unit.PX);
	durationWorstColumn.setSortable(false);

	durationWorstColumn.setFieldUpdater(new FieldUpdater<Activity, String>() {
	    @Override
	    public void update(int index, Activity activity, String value) {
		// Push the changes into the Activity Worst duration.
		durationWorstCell.clearViewData(activity);
		activity.setDurationWorst(daysIn(value));
		getUiHandlers().onDurationChange(activity);
	    }
	});

	/*
	 * Estimated duration
	 */
	final Column<Activity, String> durationEstimateColumn = new Column<Activity, String>(new TextCell()) {

	    @Override
	    public String getValue(Activity activity) {
		return round(activity.getDurationEstimate(), 2).toString() + "d";
	    }
	};
	cellTable.addColumn(durationEstimateColumn, SafeHtmlUtils.fromSafeConstant("Estimate"));
	cellTable.setColumnWidth(durationEstimateColumn, 20, Unit.PX);
	durationEstimateColumn.setSortable(false);

	/*
	 * Earliest Likely Start
	 */
	final Column<Activity, String> earliestLikelyStartColumn = new Column<Activity, String>(new TextCell()) {

	    @Override
	    public String getValue(Activity activity) {
		return round(activity.getEarliestTime(), 2).toString() + "d";
	    }
	};
	cellTable.addColumn(earliestLikelyStartColumn, SafeHtmlUtils.fromSafeConstant("Earliest Likely Start"));
	cellTable.setColumnWidth(earliestLikelyStartColumn, 20, Unit.PX);
	earliestLikelyStartColumn.setSortable(false);

	/*
	 * Latest Likely Start
	 */
	final Column<Activity, String> latestLikelyStartColumn = new Column<Activity, String>(new TextCell()) {

	    @Override
	    public String getValue(Activity activity) {
		return round(activity.getLatestTime(), 2).toString() + "d";
	    }
	};
	cellTable.addColumn(latestLikelyStartColumn, SafeHtmlUtils.fromSafeConstant("Latest Likely Start"));
	cellTable.setColumnWidth(latestLikelyStartColumn, 20, Unit.PX);
	latestLikelyStartColumn.setSortable(false);
    }

    private Double round(Double value, int places) {
	return BigDecimal.valueOf(value).setScale(places, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}
